﻿using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Text;

namespace PsInvoker
{
    /// <summary>
    /// PowerShell v 1.0 Script Invoker
    ///  just type your PowerShell command and get the results.
    /// </summary>
    public class Invoker
    {
        /// <summary>
        /// Runs a PowerShell script or cmd-let on PowerShell v. 1.0 
        /// </summary>
        /// <param name="script">a script or cmd-let to execute</param>
        /// <exception cref="PsInvokerException">Will be thrown if an error ocours</exception>
        /// <exception cref="Exception">Will wrap common Exceptions such as FileNotFoundExceptions and so on... Provides custom error messages</exception>
        /// <returns>returns the powershell result</returns>
        public String RunPowerShellScript(string script)
        {
            BindingFlags instanceMethodBindingFlags = BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance;
            StringBuilder result = new StringBuilder();
            try
            {
                // resolving progam files folder
                String programmFilesPath = Environment.GetEnvironmentVariable("ProgramFiles");

                // if environment variable isn't set throw an exception
                if (String.IsNullOrEmpty(programmFilesPath))
                    throw new PsInvokerException("PowerShell Invoker can't locate programfiles folder, Environment variable 'ProgramFiles' not set.");

                //Load the assembly for reflection
                Assembly psAssembly =
                    Assembly.LoadFrom(Path.Combine(programmFilesPath,
                                                   @"Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll"));

                // if assembly isn't set throw an Exception.
                if (psAssembly == null)
                    throw new PsInvokerException("Can't load PowerShell assembly, is PowerShell v1.0 installed?");

                var runspaceFactoryType = psAssembly.GetType("System.Management.Automation.Runspaces.RunspaceFactory");
                var psRunspace = runspaceFactoryType.InvokeMember("CreateRunspace", BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.Public, null, null, null);
                psRunspace.GetType().InvokeMember("Open", instanceMethodBindingFlags, null, psRunspace, null);

                var psPipeline = psRunspace.GetType().InvokeMember("CreatePipeline", instanceMethodBindingFlags, null, psRunspace, null);
                var psPipelineCommands = psPipeline.GetType().GetProperty("Commands").GetValue(psPipeline, null);
                psPipelineCommands.GetType().InvokeMember("AddScript", instanceMethodBindingFlags, null, psPipelineCommands, new object[] { script });
                psPipelineCommands.GetType().InvokeMember("Add", instanceMethodBindingFlags, null, psPipelineCommands,new object[] { "Out-string" });

                try
                {
                    var res = psPipeline.GetType().InvokeMember("Invoke", instanceMethodBindingFlags, null, psPipeline, null);

                    // res is an IEnumerable so it can be casted
                    foreach (var item in res as IEnumerable)
                    {
                        result.AppendLine(item.ToString());
                    }
                }
                catch (Exception cmdException)
                {
                    throw new PsInvokerException("Error while Executing CMD-Let or PS script.", cmdException);
                }
            }
            catch (FileNotFoundException)
            {
                throw new Exception("Please ensure that Microsoft PowerShell V 1.0 is installed on this system");
            }
            catch (FileLoadException)
            {
                throw new Exception("Can't load Microsoft PowerShell V 1.0.");
            }
            return result.ToString();
        }
    }
}
